﻿using System;
using System.Linq;

namespace Picnic.Spec.Specifications
{
	public class SpecificationManagerSpec : Specification
	{
		protected override void SpecificationBody()
		{
			Describe<SpecificationManager>("instance", () =>
			{
				#region Initialization

				SpecificationManager specificationManager = null;

				#endregion

				Before(() => specificationManager = new SpecificationManager());

				It("should not append null specifications", () =>
					((Action)(() => specificationManager.Add((Type)null)))
						.ShouldRaise<ArgumentException>());

				It("should not append specifications which not derived from Specification class", () =>
					((Action)(() => specificationManager.Add(typeof(string))))
						.ShouldRaise<ArgumentException>());

				It("should contains all specification statements", () =>
				{
					specificationManager.Add(typeof(Spec12));
					specificationManager.Add(typeof(Spec3));

					var scopes = specificationManager.GetStatementScopes().ToArray();
					var statements = scopes.SelectMany(s => s.Statements).ToArray();

					scopes.Length.ShouldEquals(3);
					scopes[0].Subject.ShouldEquals("Spec_1");
					scopes[1].Subject.ShouldEquals("Spec_2");
					scopes[2].Subject.ShouldEquals("Spec_3");

					statements.Length.ShouldEquals(5);
					statements[0].Text.ShouldEquals("Statement_1_1");
					statements[1].Text.ShouldEquals("Statement_1_2");
					statements[2].Text.ShouldEquals("Statement_2_1");
					statements[3].Text.ShouldEquals("Statement_2_2");
					statements[4].Text.ShouldEquals("Statement_3_1");
				});

				It("should contains dummy statements for specifications with run-time initialization errors", () =>
				{
					var type = typeof(SpecWithInitializationError);
					specificationManager.Add(type);

					var scopes = specificationManager.GetStatementScopes().ToArray();

					scopes.Length.ShouldEquals(1);
					scopes[0].Subject.ShouldEquals("Specification \"" + type.FullName + "\"");
					scopes[0].Context.ShouldEquals("initialization");
					scopes[0].Statements.Count().ShouldEquals(1);
					scopes[0].Statements.First().Text.ShouldEquals("should not raise execeptions");
					scopes[0].Statements.First().Body.ShouldRaise<OverflowException>();
				});
			});
		}

		#region Service classes

		private class Spec12 : Specification
		{
			protected override void SpecificationBody()
			{
				Describe("Spec_1", string.Empty, () =>
				{
					It("Statement_1_1", Pass);
					It("Statement_1_2", Pass);
				});

				Describe("Spec_2", string.Empty, () =>
				{
					It("Statement_2_1", Pass);
					It("Statement_2_2", Pass);
				});
			}
		}

		private class Spec3 : Specification
		{
			protected override void SpecificationBody()
			{
				Describe("Spec_3", string.Empty, () => It("Statement_3_1", Pass));
			}
		}

		private class SpecWithInitializationError : Specification
		{
			protected override void SpecificationBody()
			{
				byte b;
				checked
				{
					short s = 257;
					b = (byte)s;
				}
				Describe<byte>(b.ToString(), () => { });
			}
		}

		#endregion
	}
}